// Copyright 2023 Terramate GmbH
// SPDX-License-Identifier: MPL-2.0

package cloudstore_test

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/madlambda/spells/assert"
	"github.com/terramate-io/terramate/cloud/api/resources"
	"github.com/terramate-io/terramate/cloud/testserver/cloudstore"
	"github.com/terramate-io/terramate/errors"
)

const testUUID = "deadbeef-dead-dead-dead-deaddeafbeef"

func TestUpsertPreview(t *testing.T) {
	expectedUser := resources.User{
		UUID:        testUUID,
		Email:       "batman@terramate.io",
		DisplayName: "Batman",
		JobTitle:    "Entrepreneur",
	}

	orgUUID := resources.UUID("88ae6cb4-ee56-40aa-a024-84af44e1f5aa")
	dstore := cloudstore.Data{
		Orgs: map[string]cloudstore.Org{
			"testorg": {
				UUID:           orgUUID,
				Name:           "testorg",
				ReviewRequests: []resources.ReviewRequest{},
			},
		},
		Users: map[string]resources.User{
			"other": {
				UUID:        "88ae6cb4-ee56-40aa-a024-84af44e1f5aa",
				Email:       "other@other.io",
				DisplayName: "other",
				JobTitle:    "other",
			},
			"batman": expectedUser,
		},
	}

	preview := cloudstore.Preview{
		PreviewID:       "", // this is auto-generated by the store
		Technology:      "terraform",
		TechnologyLayer: "default",
		ReviewRequest: &resources.ReviewRequest{
			Platform:    "github",
			Repository:  "github.com/terramate-io/terramate",
			CommitSHA:   "c7641ee97388baf813d4c51b927541d2e012b116",
			Number:      42,
			Title:       "Test PR",
			Description: "Test descritiption",
			URL:         "https://github.com/terramate-io/terramate/pull/42",
		},
		StackPreviews: []*cloudstore.StackPreview{
			{
				Stack: cloudstore.Stack{
					Stack: resources.Stack{
						Repository:    "github.com/terramate-io/terramate",
						DefaultBranch: "main",
						MetaID:        "62812fa0-9e3c-4492-ace0-b5843e63fdde",
						MetaName:      "test-stack",
					},
				},
				Status: "pending",
				Cmd:    []string{"terraform", "plan", "-out=plan.out", "-detailed-exitcode"},
			},
		},
	}

	previewID, err := dstore.UpsertPreview(orgUUID, preview)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if previewID == "" {
		t.Errorf("expected previewID to be non-empty")
	}

	org := dstore.Orgs["testorg"]
	assert.EqualInts(t, 1, len(org.ReviewRequests))
	assert.EqualInts(t, 1, len(org.Previews))
	assert.EqualInts(t, 1, len(org.Stacks))
	assert.EqualInts(t, 1, len(org.Previews[0].StackPreviews))
	assert.EqualStrings(t, preview.StackPreviews[0].MetaID, org.Previews[0].StackPreviews[0].MetaID)
	assert.EqualStrings(t, preview.StackPreviews[0].Status.String(), org.Previews[0].StackPreviews[0].Status.String())
	assert.EqualInts(t, preview.ReviewRequest.Number, org.Previews[0].ReviewRequest.Number)
	assert.EqualStrings(t, previewID, org.Previews[0].PreviewID)
	assert.EqualStrings(t, preview.Technology, org.Previews[0].Technology)
	assert.EqualStrings(t, preview.TechnologyLayer, org.Previews[0].TechnologyLayer)
}

func TestGetUser(t *testing.T) {
	expectedUser := resources.User{
		UUID:        testUUID,
		Email:       "batman@terramate.io",
		DisplayName: "Batman",
		JobTitle:    "Entrepreneur",
	}
	dstore := cloudstore.Data{
		Users: map[string]resources.User{
			"other": {
				UUID:        "88ae6cb4-ee56-40aa-a024-84af44e1f5aa",
				Email:       "other@other.io",
				DisplayName: "other",
				JobTitle:    "other",
			},
			"batman": expectedUser,
		},
	}
	_, found := dstore.GetUser("nonexistent@terramate.io")
	if found {
		t.Fatal("user must not exist")
	}
	user, found := dstore.GetUser("batman@terramate.io")
	if !found {
		t.Fatal("user must exist")
	}
	if diff := cmp.Diff(expectedUser, user); diff != "" {
		t.Fatal(diff)
	}
}

func TestStoreOutputCRUD(t *testing.T) {
	const orgUUID = "88ae6cb4-ee56-40aa-a024-84af44e1f5aa"
	dstore := cloudstore.Data{}
	dstore.UpsertOrg(cloudstore.Org{
		UUID:        orgUUID,
		Name:        "testorg",
		DisplayName: "Test Org",
		Outputs:     make(map[string]resources.StoreOutput),
	})

	// Create
	output := resources.StoreOutput{
		Key: resources.StoreOutputKey{
			OrgUUID:     orgUUID,
			Name:        "output1",
			Repository:  "github.com/terramate-io/terramate",
			StackMetaID: "test-stack",
			Target:      "test-target",
		},
		Value: "output1-value",
	}
	err := dstore.InsertOutput(orgUUID, &output)
	assert.NoError(t, err)
	assert.IsTrue(t, !output.CreatedAt.IsZero())
	assert.IsTrue(t, !output.UpdatedAt.IsZero())
	assert.IsTrue(t, output.ID != "")

	// Create must fail
	err = dstore.InsertOutput(orgUUID, &output)
	assert.IsError(t, err, errors.E(cloudstore.ErrAlreadyExists))

	// Read
	readOutput, err := dstore.GetOutput(orgUUID, output.ID)
	assert.NoError(t, err)
	if diff := cmp.Diff(output, readOutput); diff != "" {
		t.Fatalf("unexpected output: %s", diff)
	}

	// Update
	err = dstore.UpdateOutputValue(orgUUID, output.ID, "updated-value")
	assert.NoError(t, err)

	// Read
	readOutput, err = dstore.GetOutput(orgUUID, output.ID)
	assert.NoError(t, err)
	assert.EqualStrings(t, "updated-value", readOutput.Value)

	// Delete
	err = dstore.DeleteOutput(orgUUID, output.ID)
	assert.NoError(t, err)

	// Read must fail
	_, err = dstore.GetOutput(orgUUID, output.ID)
	assert.IsError(t, err, errors.E(cloudstore.ErrNotExists))

	// Update must fail
	err = dstore.UpdateOutputValue(orgUUID, output.ID, "updated-value-again")
	assert.IsError(t, err, errors.E(cloudstore.ErrNotExists))

	// Delete must fail
	err = dstore.DeleteOutput(orgUUID, output.ID)
	assert.IsError(t, err, errors.E(cloudstore.ErrNotExists))
}
